#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
#include <string>
using namespace std;
using namespace muduo;

/**
 * @brief 静态方法获取实例
 *
 * @return ChatService*
 */
ChatService *ChatService::getInstance()
{
    static ChatService _service;
    return &_service;
}
/**
 * @brief 初始化：注册消息类型和回调函数的映射
 *
 */
ChatService::ChatService()
{
    // 注册回调函数
    _msgHandlerMap[LOGIN_MSG] = bind(&ChatService::login, this, _1, _2, _3);
    _msgHandlerMap[REG_MSG] = bind(&ChatService::reg, this, _1, _2, _3);
    _msgHandlerMap[LOGIN_OUT_MSG] = bind(&ChatService::loginout, this, _1, _2, _3);
    _msgHandlerMap[ONE_CHAT_MSG] = bind(&ChatService::oneChat, this, _1, _2, _3);
    _msgHandlerMap[ADD_FRIEND_MSG] = bind(&ChatService::addFriend, this, _1, _2, _3);
    _msgHandlerMap[CREATE_GROUP_MSG] = bind(&ChatService::createGroup, this, _1, _2, _3);
    _msgHandlerMap[ADD_GROUP_MSG] = bind(&ChatService::addGroup, this, _1, _2, _3);
    _msgHandlerMap[GROUP_CHAT_MSG] = bind(&ChatService::groupChat, this, _1, _2, _3);
    // 设置redis回调函数
    if (_redis.connect())
    {
        _redis.init_notify_handler(bind(&ChatService::handlerRedisSubMsg, this, _1, _2));
    }
    std::cout << "ChatService construct success!" << std::endl;
}
/**
 * @brief 获取回调方法
 *
 * @param msgid
 * @return MsgHandler
 */
MsgHandler ChatService::getHandler(int msgid)
{
    if (_msgHandlerMap.find(msgid) == _msgHandlerMap.end())
    {
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp time)
        {
            LOG_ERROR << "msgid" << msgid << " cant't not find handler!";
        };
    }
    else
    {
        return _msgHandlerMap[msgid];
    }
}
/**
 * @brief 服务器终止，更新用户状态信息
 *
 */
void ChatService::reset()
{
    _userModel.resetState();
}

/**
 * @brief 登录：对数据进行分析-是否登录成功
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();
    User usr = _userModel.query(id);
    string pwd = js["password"];
    if (usr.getId() == -1)
    {
        json resp;
        resp["msgid"] = LOGIN_MSG_ACK;
        resp["errno"] = 1;
        resp["errmsg"] = "The id is not exists!";
        conn->send(resp.dump());
    }
    else if (usr.getPwd() != pwd)
    {
        json resp;
        resp["msgid"] = LOGIN_MSG_ACK;
        resp["errno"] = 2;
        resp["errmsg"] = "The password is error!";
        conn->send(resp.dump());
    }
    else
    {
        if (usr.getState() == "online")
        {
            // 重复登录
            json resp;
            resp["msgid"] = LOGIN_MSG_ACK;
            resp["errno"] = 3;
            resp["errmsg"] = "The account already login, please not login again!";
            conn->send(resp.dump());
        }
        else
        {
            // 登录成功
            {
                // 记录登录id和链接的映射
                lock_guard<mutex> lock(_connMutex);
                _userConnMap[id] = conn;
            }
            usr.setState("online");
            _userModel.updateState(usr);
            // 订阅redis
            _redis.subscribe(id);
            json resp;
            resp["msgid"] = LOGIN_MSG_ACK;
            resp["errno"] = 0;
            resp["id"] = usr.getId();
            resp["name"] = usr.getName();
            // 拉取好友列表
            vector<User> vf = _friendModel.query(id);
            if (!vf.empty())
            {
                vector<string> friends;
                for (User &user : vf)
                {
                    json js_u;
                    js_u["id"] = user.getId();
                    js_u["name"] = user.getName();
                    js_u["state"] = user.getState();
                    friends.push_back(js_u.dump());
                }
                resp["friends"] = friends;
            }
            // 拉取群组列表
            vector<Group> vg = _groupModel.queryGroup(id);
            if (!vg.empty())
            {
                vector<string> groups;
                for (Group &group : vg)
                {
                    json js_g;
                    js_g["groupid"] = group.getId();
                    js_g["groupname"] = group.getName();
                    js_g["groupdesc"] = group.getDesc();

                    vector<string> groupusers;
                    for (GroupUser gu : group.getUsers())
                    {
                        json js_gu;
                        js_gu["id"] = gu.getId();
                        js_gu["name"] = gu.getName();
                        js_gu["state"] = gu.getState();
                        js_gu["role"] = gu.getRole();
                        groupusers.push_back(js_gu.dump());
                    }
                    js_g["groupusers"] = groupusers;
                    groups.push_back(js_g.dump());
                }
                resp["groups"] = groups;
            }
            // 拉取离线消息
            vector<string> vm = _offlineMsgModel.query(id);
            if (!vm.empty())
            {
                resp["offlinemessage"] = vm;
                _offlineMsgModel.remove(id);
            }
            conn->send(resp.dump());
        }
    }
}
/**
 * @brief 注册-是否注册成功
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    string name = js["name"];
    string pwd = js["password"];
    User user;
    user.setName(name);
    user.setPwd(pwd);
    if (_userModel.insert(user))
    {
        // 发送注册成功消息
        json resp;
        resp["msgid"] = REG_MSG_ACK;
        resp["errno"] = 0;
        resp["id"] = user.getId();
        conn->send(resp.dump());
    }
    else
    {
        // 发送注册失败消息
        json resp;
        resp["msgid"] = REG_MSG_ACK;
        resp["errno"] = 1;
        resp["errmsg"] = "the name is already exists!";
        conn->send(resp.dump());
    }
}
/**
 * @brief 一对一聊天-是否在线-数据发送到哪
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int toid = js["friendid"].get<int>();
    // 查询是否存在当前服务器上
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            it->second->send(js.dump()); // 在线直接转发
            return;
        }
    }
    // 查看是否在线-信息存在其他服务器上
    User user = _userModel.query(toid);
    if (user.getState() == "online")
    {
        // redis发布消息
        _redis.publish(toid, js.dump());
    }
    else
    {
        // 发送离线消息-数据库储存
        _offlineMsgModel.insert(toid, js.dump());
    }
}
/**
 * @brief 添加好友(默认同意)
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();
    // 添加好友关系
    _friendModel.insert(userid, friendid);
}
/**
 * @brief 创建群组调用
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    Group group;
    group.setName(js["groupname"]);
    group.setDesc(js["groupdesc"]);
    // allgroup和groupuser更新
    _groupModel.createGroup(group);
    _groupModel.addGroup(userid, group.getId(), "creator");
}
/**
 * @brief 加入群组
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    _groupModel.addGroup(userid, groupid, "normal");
}
/**
 * @brief 群组聊天
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    vector<int> users = _groupModel.queryGroupUsers(userid, groupid);
    {
        lock_guard<mutex> lock(_connMutex);
        for (int id : users)
        {
            auto it = _userConnMap.find(id);
            if (it != _userConnMap.end())
            {
                // 当前服务器上在线-直接转发消息
                it->second->send(js.dump());
            }
            else
            {
                // 查看是否在其他服务器上在线
                User user = _userModel.query(id);
                if (user.getState() == "online")
                {
                    // redis发布消息
                    _redis.publish(id, js.dump());
                }
                else
                {
                    // 发送离线消息-数据库储存
                    _offlineMsgModel.insert(id, js.dump());
                }
            }
        }
    }
}
/**
 * @brief 用户正常注销退出
 *
 * @param conn
 * @param js
 * @param time
 */
void ChatService::loginout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(userid);
        if (it != _userConnMap.end())
        {
            _userConnMap.erase(it);
        }
    }
    // 取消订阅
    _redis.unsubscribe(userid);
    //  更新用户状态
    User user;
    user.setId(userid);
    user.setState("offline");
    _userModel.updateState(user);
}
/**
 * @brief 客户端异常退出
 *
 * @param conn
 */
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{
    User user;
    {
        lock_guard<mutex> lock(_connMutex);
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); ++it)
        {
            if (it->second == conn)
            {
                user.setId(it->first);
                _userConnMap.erase(it); // 下线
                break;
            }
        }
    }
    // 更新用户状态
    if (user.getId() != -1)
    {
        // 取消订阅
        _redis.unsubscribe(user.getId());
        user.setState("offline");
        _userModel.updateState(user);
    }
}
void ChatService::handlerRedisSubMsg(int id, string msg)
{
    // 查询是否还存在当前服务器上
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(id);
        if (it != _userConnMap.end())
        {
            it->second->send(msg); // 在线直接转发
            return;
        }
    }

    // 发送离线消息-数据库储存
    _offlineMsgModel.insert(id, msg);
}